<?php

namespace service;

use Exception;
use ImalH\PDFLib\PDFLib;

class Convert
{
    /**
     * Supported formats
     */
    const FORMATS = ['dwg', 'doc', 'docx', 'wps', 'pdf', 'xlsx', 'xls', 'ppt', 'pptx'];

    /**
     * A list of files to convert
     *
     * @var array
     */
    private $fileNames = [];

    /**
     * Path of output files
     *
     * @var string
     */
    private $outputDirectory;
    /**
     * result of convert
     * @var array
     */
    private $output=[];

    /**
     * Convert constructor.
     * @param array $fileNames
     * @throws Exception
     */
    public function __construct(array $fileNames = [])
    {
        $this->checkRequirements();
        if (count($fileNames) > 0) {
            $this->addFiles($fileNames);
        }
    }

    /**
     * @return array
     */
    public function getCovertResult()
    {
        return $this->output;
    }

    /**
     * @return bool
     * @throws Exception
     */
    public function checkRequirements()
    {
        // Check OS
        if(PHP_OS != 'Linux') {
            throw new Exception('Operating system is not supported. The program only runs on Linux server.');
        }

        // Check if java has been installed
        exec('java -version > NULL && echo yes || echo no',$output,$return_value);
        if($return_value) {
            throw new Exception('The progress is java killed.');
        }
        if($output[0]=='no') {
            throw new Exception('There is no Java environment.');
        }
        // Check if liberoffice has been installed
        exec('libreoffice --version > NULL && echo yes || echo no',$output,$return_value);
        if($return_value) {
            throw new Exception('The progress libreoffice is killed.');
        }
        if($output[0] == 'no') {
            throw new Exception('LibreOffice has not been installed.');
        }
    }

    /**
     * @param string $fileName
     * @return bool
     * @throws Exception
     */
    public function isSupported(string $fileName): bool
    {
        if (!is_file($fileName)) {
            throw new Exception('The file ' . $fileName . ' does not exist.');
        }

        $fileInfo   = pathinfo($fileName);
        $fileFormat = $fileInfo['extension'];
        return in_array($fileFormat, self::FORMATS);
    }

    /**
     * @param string $path 目录
     * @return object
     * @throws Exception
     */
    public function setOutputDir(string $path)
    {
        try{
            if (!is_dir($path)) {
                if (!mkdir($path)) {
                    throw new Exception('Cannot create path "' . $path .'".');
                }
            }
            $this->outputDirectory = (substr($path, -1) !== '/') ? $path . '/' : $path;
            return $this;
        }catch (Exception $ex){
            throw new Exception('Not a path');
        }
    }

    /**
     * @param string $fileName
     * @return $this
     * @throws Exception
     */
    public function addFile(string $fileName)
    {
        if($this->isSupported($fileName)) {
            $this->fileNames[] = $fileName;
            return $this;
        }else{
            throw new Exception('Failed to add "' . basename($fileName) . '". The format is not supported.');
        }
    }

    /**
     * @param array $fileNames
     * @return object
     * @throws Exception
     */
    public function addFiles(array $fileNames)
    {
        foreach ($fileNames as $file) {
            $this->addFile($file);
        }
        return $this;
    }

    /**
     * @param string $destination
     * @return object
     * @throws Exception
     */
    public function convert(string $destination)
    {
        $this->outputDirectory = !is_null($destination) ? $destination : $this->outputDirectory;

        $this->setOutputDir($this->outputDirectory);

        $successCount = 0;

        if (is_dir($this->outputDirectory) === false) {
            throw new Exception('Directory "' . $this->outputDirectory . '" does not exist.');
        }

        foreach($this->fileNames as $file) {
            if (!file_exists($file)) {
                throw new Exception('File "' . $file . '" does not exist.');
            }

            try {
//                Log::notice('开始转换文件{basename}',$fileParts);
                $command = '/usr/bin/libreoffice --invisible --convert-to pdf '.$file.' --outdir '.$this->outputDirectory;
                exec($command);
                $successCount++;
                $fileParts = pathinfo($file);
                $output    = $this-> outputDirectory . $fileParts['filename'] . '.pdf';
                $this->output[$successCount] =  $output;
//                Log::notice('转换成功'.$this->outputDirectory.$fileParts['basename']);
            } catch (Exception $e) {
                throw new Exception('File "' . $file . '" does not exist.');
            }
        }
        return $this;
    }

    /**
     * @param string|null $path
     * @param string|null $destination
     * @param array $arr
     * @return object
     * @throws Exception
     */
    public function convertCover(string $path=null,string $destination=null,array &$arr): object {
        $this->outputDirectory = !is_null($destination) ? $destination : $this->outputDirectory;
        if (is_dir($this->outputDirectory) === false) {
            throw new Exception('Directory "' . $this->outputDirectory . '" does not exist.');
        }
        if($path){
            $fileParts = pathinfo($path);
            try {
                $command = '/usr/bin/libreoffice --invisible --convert-to pdf '.$path.' --outdir '.$this->outputDirectory;
                @exec($command,$output,$return_value);
                if($return_value>0){
                    throw new Exception('process is killed.');
                }
                $_path = $fileParts['dirname'].DIRECTORY_SEPARATOR.$fileParts['filename'].'.pdf';
                $prefix = $fileParts['filename'].'_cover_';
                $pdf = new PDFLib();
                $pdf->setPdfPath($_path)
                    ->setPageRange(1,1)
                    ->setImageQuality(100)
                    ->setImageFormat(PDFLib::$IMAGE_FORMAT_PNG,$dDownScaleFactor=null)
                    ->setOutputPath($this->outputDirectory)
                    ->setFilePrefix($prefix)
                    ->convert();
                $arr = [
                    'source'=> $path,
                    'pdf'=> $_path,
                    'cover'=>$fileParts['dirname'].$prefix.'1.png'
                ];
            } catch (Exception $e) {
                throw new Exception($e);
            }
        }else{
            foreach($this->fileNames as $file) {
                if (!file_exists($file)) {
                    throw new Exception('File "' . $file . '" does not exist.');
                }
                $fileParts = pathinfo($file);
                try {
                    $command = '/usr/bin/libreoffice --invisible --convert-to pdf '.$file.' --outdir '.$this->outputDirectory;
                    @exec($command,$output,$return_value);
                    if($return_value>0){
                        throw new Exception('process is killed.');
                    }
                    $path = $fileParts['dirname'].DIRECTORY_SEPARATOR.$fileParts['filename'].'.pdf';
                    $prefix = $fileParts['filename'].'_cover_';
                    $pdf = new PDFLib();
                    $pdf->setPdfPath($path)
                        ->setPageRange(1,1)
                        ->setImageQuality(100)
                        ->setImageFormat(PDFLib::$IMAGE_FORMAT_PNG,$dDownScaleFactor=null)
                        ->setOutputPath($this->outputDirectory)
                        ->setFilePrefix($prefix)
                        ->convert();
                    array_push($arr,[
                        'source'=> $file,
                        'pdf'=> $path,
                        'cover'=>$fileParts['dirname'].$prefix.'1.png'
                    ]);
                } catch (Exception $e) {
                    throw new Exception($e);
                }
            }
        }
        return $this;
    }

    /**
     * @param string|null $dir
     * @param string|null $output_dir
     * @param array|null $output
     * @return $this
     * @throws Exception
     */
    public function convertDir(string $dir=null,string $output_dir=null,array $output=null)
    {
        $dir = !is_null($dir) ? $dir : $this->outputDirectory;
        $output_dir = is_null($output_dir)?$output_dir:$this->outputDirectory;
        if (is_dir($dir) === false) {
            throw new Exception("Directory {$dir} does not exist.");
        }
        $file = get_files($dir);
        foreach ($file as $k => $v){
            $fileParts = pathinfo($v);
            if (!file_exists($v)) {
                $output[$k] = [
                    'status'=>0,
                    'msg'=>"File {$v} does not exist.",
                    'source'=> $v,
                    'path'=> ''
                ];
            }else{
                try {
                    $command = '/usr/bin/libreoffice --invisible --convert-to pdf ' . $v . ' --outdir ' . $output_dir;
                    @exec($command, $output, $return_value);
                    if ($return_value > 0) {
                        throw new Exception('process is killed.');
                    }
                    $path = $fileParts['dirname'] . DIRECTORY_SEPARATOR.$fileParts['filename'] . '.pdf';
                    $output[$k]=[
                        'status'=>0,
                        'msg'=>"File {$v} is convert success.",
                        'source'=> $v,
                        'path'=> $path
                    ];
                }catch (Exception $ex){
                    $output[$k] = [
                        'status'=>0,
                        'msg'=>"File {$v} is convert failed.",
                        'source'=> $v,
                        'path'=> ''
                    ];
                }
            }
        }
        $this->output = $output;
        return $this;
    }
}